package com.ndp.fb.fbagent.service.help;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ndp.fb.constant.CommonConstants;
import com.ndp.fb.enums.NotificationMessageKey;
import com.ndp.fb.enums.message.ChannelType;
import com.ndp.fb.fbagent.service.OauthFbService;
import com.ndp.fb.model.fbagent.result.entrustaccount.TokenResult;
import com.ndp.fb.mq.api.producer.EventNotificationMessageProducerService;
import com.ndp.fb.rdb.api.FbUserRdbService;
import com.ndp.fb.rdb.model.AppInfo;
import com.ndp.fb.rdb.model.FbUser;
import com.ndp.fb.redis.api.RedisClient;
import com.ndp.fb.redis.model.RedisParams;
import com.ndp.fb.util.DateFormatUtil;
import com.ndp.fb.util.StringUtil;
import com.restfb.WebRequestor;
import com.restfb.util.StringUtils;
import com.restfb.util.UrlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by mikey.zhaopeng on 2015/7/8.
 */
@Component
public class CheckAccessTokenHelper {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Reference
    FbUserRdbService fbUserRdbService;
    @Autowired
    EventNotificationMessageProducerService eventNotificationMessageProducerService;
    @Autowired
    private OauthFbService oauthFbService;

    @Autowired
    private RedisClient redisClient;

    /**
     * 检查本地token的标志位是否失效
     * 返回 空正常,否则返回错误信息
     *
     * @return
     */
    public String checkAccessToken(String url) {
        String result = null;
        if (StringUtils.isBlank(url)) {
            return result;
        }
        if (url.contains("debug_token")) {
            logger.warn("不对此 debug_token 进行fb的token检查!");
            return result;
        }
        boolean isInvalid = false;
        String accessToken = null;
        try {
            accessToken = this.getAccessToken(url);
            if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {
                logger.warn("accessToken is null! " + url);
                return this.getNullTokenMsg();
            }
            isInvalid = fbUserRdbService.getIsInvalidByAcessToken(accessToken);
        } catch (Exception e) {
            isInvalid = false;
            logger.error(e.getMessage(), e);
        }
        if (!isInvalid) {
            return result;
        }
        return this.getInvalidTokenMsg(accessToken);
    }

    public String checkAccessToken(String url, String params) {
        String result = null;
        if (StringUtils.isBlank(url) || StringUtils.isBlank(params)) {
            return result;
        }
        if (url.contains("debug_token")) {
            logger.warn("不对此 debug_token 进行fb的token检查!");
            return result;
        }
        boolean isInvalid = false;
        String accessToken = null;
        try {
            accessToken = this.getAccessToken(url, params);
            if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {
                logger.warn("accessToken is null! url:" + url + " params:" + params);
                return this.getNullTokenMsg();
            }
            isInvalid = fbUserRdbService.getIsInvalidByAcessToken(accessToken);
        } catch (Exception e) {
            isInvalid = false;
            logger.error(e.getMessage(), e);
        }
        if (!isInvalid) {
            return result;
        }
        return this.getInvalidTokenMsg(accessToken);
    }

    public String getAccessToken(String url, String parameters) {
        String result = null;
        result = this.getAccessToken(url);
        if (StringUtils.isBlank(result)) {
            result = this.getAccessTokenFromParams(parameters);
        }
        return result;
    }

    /**
     * 检查本地token的标志位是否失效
     * 返回 空正常,否则返回错误信息
     *
     * @return
     */
    public String checkAccessToken(RedisParams redisParams) {
        String result = null;
        boolean isInvalid = false;
        String accessToken = null;
        try {
            accessToken = this.getAccessToken(redisParams);
            if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {
                if (redisParams != null) {
                    logger.warn("accessToken is null! redisParams:" + JSON.toJSONString(redisParams));
                } else {
                    logger.warn("accessToken is null! redisParams is null too.");
                }
                return this.getNullTokenMsg();
            }
            isInvalid = fbUserRdbService.getIsInvalidByAcessToken(accessToken);
        } catch (Exception e) {
            isInvalid = false;
            logger.error(e.getMessage(), e);
        }
        if (!isInvalid) {
            return result;
        }
        return this.getInvalidTokenMsg(accessToken);
    }

    /**
     * 从 url 解析得到 accessToken
     *
     * @param url
     * @return
     */
    public String getAccessToken(String url) {
        return this.getAccessTokenFromUrl(url);
    }


    /**
     * 从 redisParams 解析得到 accessToken
     *
     * @param redisParams
     * @return
     */
    public String getAccessToken(RedisParams redisParams) {
        String accessToken = null;
        if (redisParams == null) {
            return accessToken;
        }
        accessToken = this.getAccessTokenFromUrl(redisParams.getUrl());
        if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {//如果 url 获取不到 accessToken ,则从 params 中获取
            accessToken = this.getAccessTokenFromParams(redisParams.getParams());
        }
        return accessToken;
    }

    /**
     * 从url中获取accesstoken
     *
     * @param url
     * @return
     */
    private String getAccessTokenFromUrl(String url) {
        String accessToken = null;
        if (org.apache.commons.lang3.StringUtils.isEmpty(url)) {
            return accessToken;
        }

        Map<String, List<String>> urlData = UrlUtils.extractParametersFromUrl(url);
        if (urlData == null) {
            return accessToken;
        }
        List<String> list = urlData.get("access_token");
        if (CollectionUtils.isEmpty(list)) {
            return accessToken;
        }
        accessToken = list.get(0);
        if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {
            return accessToken;
        }
        return accessToken;
    }

    /**
     * 从 params 中获取 accesstoken
     *
     * @param params
     * @return
     */
    private String getAccessTokenFromParams(String params) {
        String accessToken = null;
        if (org.apache.commons.lang3.StringUtils.isEmpty(params)) {
            return accessToken;
        }
        if (!params.contains("access_token")) {
            return accessToken;
        }
        Map<String, List<String>> paramsData = UrlUtils.extractParametersFromUrl(params);
        List<String> paramsList = paramsData.get("access_token");
        if (CollectionUtils.isEmpty(paramsList)) {
            String[] arr$ = params.split("&");
            int len$ = arr$.length;

            for (int i$ = 0; i$ < len$; ++i$) {
                String param = arr$[i$];
                String[] pair = param.split("=");
                String key = UrlUtils.urlDecode(pair[0]);
                String value = "";
                if (pair.length > 1) {
                    value = UrlUtils.urlDecode(pair[1]);
                }
                if ("access_token".equalsIgnoreCase(key)) {
                    accessToken = value;
                    if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {
                        return accessToken;
                    }
                    break;
                }
            }
            return accessToken;
        }
        accessToken = paramsList.get(0);
        if (org.apache.commons.lang3.StringUtils.isEmpty(accessToken)) {
            return accessToken;
        }
        return accessToken;
    }

    /**
     * 检查 Response 是否有错,如果有,
     *
     * @param response
     * @return
     */
    public WebRequestor.Response checkFBResponse(WebRequestor.Response response, String url, String params) {
        try {
            String body = null;
            Integer statusCode = 0;
            if (null == response) {
                logger.error("fb resule >>>> response is null");
                body = "{\"error\":\"fb resule >>>> response is null\",\"error_subcode\":90000000}";
                response = new WebRequestor.Response(200, body);
            } else {
                body = response.getBody();
                if (!StringUtils.isBlank(body)) {
                    if (body.startsWith("{")) {
                        int e = Math.min(50, body.length());
                        if (body.substring(0, e).contains("\"error\"")) {
                            JSONObject jsonObject = JSON.parseObject(body);
                            if (jsonObject.containsKey("error")) {
                                JSONObject errorObject = jsonObject.getJSONObject("error");
                                String code = errorObject.getString("code");
                                String errorSubCode = errorObject.getString("error_subcode");
                                // 检查一下token是否真的失效了,失效了就把数据库置为失效
//                                if ("460".equals(errorSubCode)) {// 190-460 错误信息 : "message":"Error validating access token: The session has been invalidated because the user has changed the password.", "type":"OAuthException","code":190,"error_subcode":460
                                if ("190".equals(code)) {//判断 token 失效
                                    String inputToken = this.getAccessToken(url, params);
                                    this.checkFBAccessToken(inputToken);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            return response;
        }
    }

    /**
     * 向 fb 发出请求,检查 token 是否失效
     *
     * @param inputToken
     * @return
     */

    public void checkFBAccessToken(String inputToken) {
        if (StringUtil.isEmpty(inputToken)) return;
        if (!this.redisClient.hasKey(CommonConstants.APPINFO_KEY)) return;
        else {
            AppInfo appInfo = (AppInfo) this.redisClient.get(CommonConstants.APPINFO_KEY);
            if (appInfo == null) return;
            String appToken = appInfo.getAppToken();
            if (StringUtil.isEmpty(appToken)) return;
            TokenResult tokenResult = oauthFbService.checkToken(appToken, inputToken);
            if (!tokenResult.isValid()) {//如果失效,将数据库置为失效
                //去重
                if (!fbUserRdbService.getIsInvalidByAcessToken(inputToken)) {
                    fbUserRdbService.updateInValidByAcessToken(inputToken, "1");
                    fbUserRdbService.clearValidByFbAccessToken(inputToken);
                    sendInvalidTokenMsg(inputToken, tokenResult);
                }
            }
        }
    }
    
    /**
     * 给消息中心发送token失效的消息，支持网页和短信形式。
     *
     * @param inputToken
     * @param tokenResult fb 返回的token验证结果。
     * @author jacky.cheng
     */
    private void sendInvalidTokenMsg(String inputToken, TokenResult tokenResult) {
//        StringBuffer msg = new StringBuffer();
//        msg.append("您的应用：");
//        msg.append("(appId:" + tokenResult.getAppId() + ")");
//        msg.append(tokenResult.getApplication());
//        msg.append("的token在" + DateFormatUtil.formatLong16(new Date()) + "时失效。");
//        msg.append("userId:" + tokenResult.getUserId() + "。");
//        FbUser fbUser = fbUserRdbService.getFbUserByFbUserId(Long.valueOf(tokenResult.getUserId()));
//        List<ChannelType> channelTypes = new ArrayList<>();
//        channelTypes.add(ChannelType.WEBSITE);
//        eventNotificationMessageProducerService.sendMessage(fbUser.getUserId(), msg.toString(), EventNotificationType.TOKEN_INVALID, "/entrustaccounts/entrustAccountList", channelTypes);

        //您的应用：(appId:{0}){1}的token在{2}时失效。userId:{3}。
        List<String> eventValues = new ArrayList<>();
        eventValues.add(tokenResult.getAppId());
        eventValues.add(tokenResult.getApplication());
        eventValues.add(DateFormatUtil.formatLong16(new Date()));
        eventValues.add(tokenResult.getUserId());
        FbUser fbUser = fbUserRdbService.getFbUserByFbUserId(Long.valueOf(tokenResult.getUserId()));
        eventNotificationMessageProducerService.sendMessage(fbUser.getUserId(), NotificationMessageKey.N_006, eventValues,"#!account");
        List<ChannelType> channelTypeList = new ArrayList<>();
        channelTypeList.add(ChannelType.EMAIL);
        channelTypeList.add(ChannelType.SMS);
        eventNotificationMessageProducerService.sendMonitorMessage("DEVOWNEREMAIL,PRODUCTOWNEREMAIL,DEVOWNERPHONE,PRODUCTOWNERPHONE", tokenResult.getError(), channelTypeList);
    }


    public String getInvalidTokenMsg(String accessToken) {
        return "{\"error\": {\"message\": \" 检查 token 失效,暂停此 " + accessToken + " accesstoken 的请求发送! \", \"error_subcode\": \"90000003\", \"type\": \"OAuthException\"} }";
    }

    public String getNullTokenMsg() {
        return "{\"error\": {\"message\": \"  token is null, could not send request!!!!!!  \", \"error_subcode\": \"90000004\", \"type\": \"OAuthException\"} }";
    }

}
